#pragma once
#include<iostream>
#include "log.hpp"
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include<strings.h>
#include<functional>
#include<unordered_map>
typedef function<std::string(std::string&)> func_t;
 uint16_t defaultport_=8088;
 std::string defaultip_="0.0.0.0";//"127.0.0.1" - 本地测试
Log lg;
enum{
    SOCK_ERR=1,
    BIND_ERR
};
class UdpServer
{
    private:
    int sockfd_;//网络文件描述符
    uint16_t port_;//端口号
    std::string ip_;//ip地址
    bool running_;
    std::unordered_map<std::string,sockaddr_in> online_uer;
    public:
    UdpServer(const uint16_t& port=defaultport_,const string& defaultip=defaultip_)
    :sockfd_(0),port_(port),ip_(defaultip),running_(false)
    {

    }
    void Init()
    {
        //1、创建套接字
        sockfd_=socket(AF_INET,SOCK_DGRAM,0);//AF_INET: 域，SOCK_DGRAM: udp
        if(sockfd_<0)
        {   //打印错误日志
            lg(FATAL,"make socket error! errnum is: %d , errmessage is: %s",sockfd_,strerror(sockfd_));
            exit(SOCK_ERR);
        }
        lg(INFO," Socket sucess !! sockfd is:%d",sockfd_);
        //准备好数据
        struct sockaddr_in local;
        bzero(&local,sizeof(local));//初始化sockaddr_in
        local.sin_family=AF_INET;//表明自己的类型
        local.sin_port=htons(port_);//端口，需要主机转网络，大小端转换（保证大端）
        local.sin_addr.s_addr=inet_addr(ip_.c_str());//ip地址由字符串转32位无符号整形
        //local.sin_addr.s_addr=htonl(INADDR_ANY);//或者写成

        //2.绑定套接字
        int n=bind(sockfd_,(const struct sockaddr*)&local,sizeof(local));
        if(n<0)
        {
            lg(FATAL,"bind false ,errno is :%d, errmessage is:%s",n,strerror(n));
            exit(BIND_ERR);
        }
        //绑定成功
        lg(INFO,"Bind sucess !! errno is: %d, errmessage is: %s",n,strerror(n));
    }
    void CheckUser(const sockaddr_in& client,const std::string&ip,const uint16_t& port)
    {
        auto iter=online_uer.find(ip);//会返回找到的key的迭代器，找不到返回end();
        if(iter==online_uer.end())
        {
            //说明用户列表里没有，说明用户刚登陆,添加到列表
            online_uer.insert({ip,client});
            //打印一句用户已添加
            cout<<"["<<ip<<":"<<port<<"]"<<" is add to userlist!!"<<endl;
        }
    }
    void Broadcast(const string&info,const std::string&ip,const uint16_t& port)
    {
        for(auto &user:online_uer)
        {
        string message="[";
        message+=ip;
        message+=":";
        message+=std::to_string(port);
        message+="]#";
        message+=info;
        socklen_t len=sizeof(user.second);
        //发送
        sendto(sockfd_,message.c_str(),message.size(),0,(struct sockaddr*)(&user.second),len);
        }
    }
    void Run()
    {
        running_=true;
        char buffer[4096]={0};
        
        while(running_)
        {
            memset(buffer,0,sizeof(buffer));
            struct sockaddr_in client_addr;//用来获取对方信息（发送方）
        socklen_t addrlen=sizeof(client_addr);
        //从套接字里获取信息并返回给对方
        ssize_t n=recvfrom(sockfd_,buffer,sizeof(buffer)-1,0,(sockaddr*)&client_addr,&addrlen);
        if(n<0)
        {
            lg(WARNING,"Recvfrom error!! errno is : %d, errmessage is : %s ",n,strerror(n));
            continue;//失败继续获取
        }
        //获取成功
        //通过sockaddr_in结构体获取客户端的ip+端口
        uint16_t port=ntohs(client_addr.sin_port);//网络转主机字节序
        std::string ip=inet_ntoa(client_addr.sin_addr);//ip地址转字符串
         CheckUser(client_addr,ip,port);//通过key-ip、 value- sockaddr_in 检查用户是否已登陆
        std::string info=buffer;
         Broadcast(info,ip,port);//包装信息，广播，转发
        }
    }
    ~UdpServer()
    {
        if(sockfd_>0)
        close(sockfd_);
    }
};